home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Celestin Apprentice 5
/
Apprentice-Release5.iso
/
Source Code
/
C
/
Applications
/
Python 1.3.3
/
Python 133 SRC
/
Extensions
/
img
/
genmap.c
< prev
next >
Wrap
Text File
|
1995-12-21
|
10KB
|
371 lines
/*
** genmap - Generate a colormap with the specified number of
** entries.
**
** Jack Jansen, CWI, 1995.
**
** Modified from ppmquant.c, which is
**
** Copyright (C) 1989, 1991 by Jef Poskanzer.
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notice appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation. This software is provided "as is" without express or
** implied warranty.
*/
#include "Python.h"
#include "mppmcmap.h"
#define MAXCOLORS 32767
/* #define LARGE_NORM */
#define LARGE_LUM
/* #define REP_CENTER_BOX */
/* #define REP_AVERAGE_COLORS */
#define REP_AVERAGE_PIXELS
typedef struct box* box_vector;
struct box
{
int ind;
int colors;
int sum;
};
typedef int (*qsfuncptr)Py_PROTO((const void *, const void *));
static int mediancut Py_PROTO((colorhist_vector chv, int colors, int sum,
int newcolors, pixel *colormap));
static int redcompare Py_PROTO((colorhist_vector ch1, colorhist_vector ch2));
static int greencompare Py_PROTO((colorhist_vector ch1, colorhist_vector ch2));
static int bluecompare Py_PROTO((colorhist_vector ch1, colorhist_vector ch2));
static int sumcompare Py_PROTO((box_vector b1, box_vector b2));
int
mppm_genmap(pixels, cols, rows, mapdata, newcolors)
long *pixels;
int cols, rows;
long *mapdata;
int newcolors;
{
#if 0
int argc;
char* argv[];
{
FILE* ifp;
pixel** pixels;
pixel** mappixels;
register pixel* pP;
int argn, rows, cols, maprows, mapcols, row;
register int col, limitcol;
pixval maxval, newmaxval, mapmaxval;
int newcolors, colors;
register int ind;
colorhash_table cht;
int floyd;
int usehash;
long* thisrerr;
long* nextrerr;
long* thisgerr;
long* nextgerr;
long* thisberr;
long* nextberr;
long* temperr;
register long sr, sg, sb, err;
#define FS_SCALE 1024
int fs_direction;
char* usage = "[-floyd|-fs] <ncolors> [ppmfile]\n [-floyd|-fs] -map mapfile [ppmfile]";
#endif
colorhist_vector chv;
int colors, rv;
/*
** Step 2: attempt to make a histogram of the colors, unclustered.
*/
chv = mppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &colors );
if ( chv == (colorhist_vector) 0 ) {
return 0;
#if 0
XXXX this code should go to a separate 'scale' routine.
pm_message( "too many colors!" );
newmaxval = maxval / 2;
pm_message(
"scaling colors from maxval=%d to maxval=%d to improve clustering...",
maxval, newmaxval );
for ( row = 0; row < rows; ++row )
for ( col = 0, pP = pixels[row]; col < cols; ++col, ++pP )
MPPM_DEPTH( *pP, *pP, maxval, newmaxval );
maxval = newmaxval;
#endif
}
/*
** Step 3: apply median-cut to histogram, making the new colormap.
*/
rv = mediancut( chv, colors, rows * cols, newcolors, mapdata );
mppm_freecolorhist( chv );
return rv;
}
/*
** Here is the fun part, the median-cut colormap generator. This is based
** on Paul Heckbert's paper "Color Image Quantization for Frame Buffer
** Display", SIGGRAPH '82 Proceedings, page 297.
*/
#if __STDC__
static int
mediancut( colorhist_vector chv, int colors, int sum,
int newcolors, pixel *colormap )
#else /*__STDC__*/
static int
mediancut( chv, colors, sum, newcolors, colormap )
colorhist_vector chv;
int colors, sum, newcolors;
pixel *colormap;
#endif /*__STDC__*/
{
box_vector bv;
register int bi, i;
int boxes;
bv = (box_vector) malloc( sizeof(struct box) * newcolors );
if ( bv == (box_vector) 0 ) {
return -1;
}
/*
** Set up the initial box.
*/
bv[0].ind = 0;
bv[0].colors = colors;
bv[0].sum = sum;
boxes = 1;
/*
** Main loop: split boxes until we have enough.
*/
while ( boxes < newcolors )
{
register int indx, clrs;
int sm;
register int minr, maxr, ming, maxg, minb, maxb, v;
int halfsum, lowersum;
/*
** Find the first splittable box.
*/
for ( bi = 0; bi < boxes; ++bi )
if ( bv[bi].colors >= 2 )
break;
if ( bi == boxes )
break; /* ran out of colors! */
indx = bv[bi].ind;
clrs = bv[bi].colors;
sm = bv[bi].sum;
/*
** Go through the box finding the minimum and maximum of each
** component - the boundaries of the box.
*/
minr = maxr = MPPM_GETR( chv[indx].color );
ming = maxg = MPPM_GETG( chv[indx].color );
minb = maxb = MPPM_GETB( chv[indx].color );
for ( i = 1; i < clrs; ++i )
{
v = MPPM_GETR( chv[indx + i].color );
if ( v < minr ) minr = v;
if ( v > maxr ) maxr = v;
v = MPPM_GETG( chv[indx + i].color );
if ( v < ming ) ming = v;
if ( v > maxg ) maxg = v;
v = MPPM_GETB( chv[indx + i].color );
if ( v < minb ) minb = v;
if ( v > maxb ) maxb = v;
}
/*
** Find the largest dimension, and sort by that component. I have
** included two methods for determining the "largest" dimension;
** first by simply comparing the range in RGB space, and second
** by transforming into luminosities before the comparison. You
** can switch which method is used by switching the commenting on
** the LARGE_ defines at the beginning of this source file.
*/
#ifdef LARGE_NORM
if ( maxr - minr >= maxg - ming && maxr - minr >= maxb - minb )
qsort(
(char*) &(chv[indx]), clrs, sizeof(struct colorhist_item),
(qsfuncptr)redcompare );
else if ( maxg - ming >= maxb - minb )
qsort(
(char*) &(chv[indx]), clrs, sizeof(struct colorhist_item),
(qsfuncptr)greencompare );
else
qsort(
(char*) &(chv[indx]), clrs, sizeof(struct colorhist_item),
(qsfuncptr)bluecompare );
#endif /*LARGE_NORM*/
#ifdef LARGE_LUM
{
pixel p;
float rl, gl, bl;
MPPM_ASSIGN(p, maxr - minr, 0, 0);
rl = MPPM_LUMIN(p);
MPPM_ASSIGN(p, 0, maxg - ming, 0);
gl = MPPM_LUMIN(p);
MPPM_ASSIGN(p, 0, 0, maxb - minb);
bl = MPPM_LUMIN(p);
if ( rl >= gl && rl >= bl )
qsort(
(char*) &(chv[indx]), clrs, sizeof(struct colorhist_item),
(qsfuncptr)redcompare );
else if ( gl >= bl )
qsort(
(char*) &(chv[indx]), clrs, sizeof(struct colorhist_item),
(qsfuncptr)greencompare );
else
qsort(
(char*) &(chv[indx]), clrs, sizeof(struct colorhist_item),
(qsfuncptr)bluecompare );
}
#endif /*LARGE_LUM*/
/*
** Now find the median based on the counts, so that about half the
** pixels (not colors, pixels) are in each subdivision.
*/
lowersum = chv[indx].value;
halfsum = sm / 2;
for ( i = 1; i < clrs - 1; ++i )
{
if ( lowersum >= halfsum )
break;
lowersum += chv[indx + i].value;
}
/*
** Split the box, and sort to bring the biggest boxes to the top.
*/
bv[bi].colors = i;
bv[bi].sum = lowersum;
bv[boxes].ind = indx + i;
bv[boxes].colors = clrs - i;
bv[boxes].sum = sm - lowersum;
++boxes;
qsort( (char*) bv, boxes, sizeof(struct box), (qsfuncptr)sumcompare );
}
/*
** Ok, we've got enough boxes. Now choose a representative color for
** each box. There are a number of possible ways to make this choice.
** One would be to choose the center of the box; this ignores any structure
** within the boxes. Another method would be to average all the colors in
** the box - this is the method specified in Heckbert's paper. A third
** method is to average all the pixels in the box. You can switch which
** method is used by switching the commenting on the REP_ defines at
** the beginning of this source file.
*/
for ( bi = 0; bi < boxes; ++bi )
{
#ifdef REP_CENTER_BOX
register int indx = bv[bi].ind;
register int clrs = bv[bi].colors;
register int minr, maxr, ming, maxg, minb, maxb, v;
minr = maxr = MPPM_GETR( chv[indx].color );
ming = maxg = MPPM_GETG( chv[indx].color );
minb = maxb = MPPM_GETB( chv[indx].color );
for ( i = 1; i < clrs; ++i )
{
v = MPPM_GETR( chv[indx + i].color );
minr = min( minr, v );
maxr = max( maxr, v );
v = MPPM_GETG( chv[indx + i].color );
ming = min( ming, v );
maxg = max( maxg, v );
v = MPPM_GETB( chv[indx + i].color );
minb = min( minb, v );
maxb = max( maxb, v );
}
MPPM_ASSIGN(
colormap[bi], ( minr + maxr ) / 2, ( ming + maxg ) / 2,
( minb + maxb ) / 2 );
#endif /*REP_CENTER_BOX*/
#ifdef REP_AVERAGE_COLORS
register int indx = bv[bi].ind;
register int clrs = bv[bi].colors;
register long r = 0, g = 0, b = 0;
for ( i = 0; i < clrs; ++i )
{
r += MPPM_GETR( chv[indx + i].color );
g += MPPM_GETG( chv[indx + i].color );
b += MPPM_GETB( chv[indx + i].color );
}
r = r / clrs;
g = g / clrs;
b = b / clrs;
MPPM_ASSIGN( colormap[bi], r, g, b );
#endif /*REP_AVERAGE_COLORS*/
#ifdef REP_AVERAGE_PIXELS
register int indx = bv[bi].ind;
register int clrs = bv[bi].colors;
register long r = 0, g = 0, b = 0, sum = 0;
for ( i = 0; i < clrs; ++i )
{
r += MPPM_GETR( chv[indx + i].color ) * chv[indx + i].value;
g += MPPM_GETG( chv[indx + i].color ) * chv[indx + i].value;
b += MPPM_GETB( chv[indx + i].color ) * chv[indx + i].value;
sum += chv[indx + i].value;
}
r = r / sum;
if ( r > MAXVAL ) r = MAXVAL; /* avoid math errors */
g = g / sum;
if ( g > MAXVAL ) g = MAXVAL;
b = b / sum;
if ( b > MAXVAL ) b = MAXVAL;
MPPM_ASSIGN( colormap[bi], r, g, b );
#endif /*REP_AVERAGE_PIXELS*/
}
/*
** All done.
*/
return newcolors;
}
static int
redcompare( ch1, ch2 )
colorhist_vector ch1, ch2;
{
return (int) MPPM_GETR( ch1->color ) - (int) MPPM_GETR( ch2->color );
}
static int
greencompare( ch1, ch2 )
colorhist_vector ch1, ch2;
{
return (int) MPPM_GETG( ch1->color ) - (int) MPPM_GETG( ch2->color );
}
static int
bluecompare( ch1, ch2 )
colorhist_vector ch1, ch2;
{
return (int) MPPM_GETB( ch1->color ) - (int) MPPM_GETB( ch2->color );
}
static int
sumcompare( b1, b2 )
box_vector b1, b2;
{
return b2->sum - b1->sum;
}